package com.example.venue_reservation_service.utils;

import com.example.venue_reservation_service.vo.excel.ReservationExcel;

import java.util.*;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.stream.Collectors;

public class VenueRecommender {



    // 用户-物品评分矩阵
    private Map<Integer, Map<Integer, Double>> userVenueScores;
    // 物品-物品相似度矩阵
    private Map<Integer, Map<Integer, Double>> venueSimilarities;
    // 所有预约记录
    private List<ReservationExcel> allReservations;
    // 目标用户ID
    private int targetUserId;

    public VenueRecommender(List<ReservationExcel> reservations, int targetUserId) {
        this.allReservations = reservations;
        this.targetUserId = targetUserId;
        this.userVenueScores = new HashMap<>();
        this.venueSimilarities = new HashMap<>();
    }

    // 构建用户-物品评分矩阵
    private void buildUserVenueMatrix() {
        // 按用户分组
        Map<Integer, List<ReservationExcel>> reservationsByUser = allReservations.stream()
                .collect(Collectors.groupingBy(ReservationExcel::getUserId));

        for (Map.Entry<Integer, List<ReservationExcel>> entry : reservationsByUser.entrySet()) {
            int userId = entry.getKey();
            List<ReservationExcel> userReservations = entry.getValue();

            // 按场地分组，计算用户对每个场地的偏好得分
            Map<Integer, Long> venueCounts = userReservations.stream()
                    .collect(Collectors.groupingBy(ReservationExcel::getVenueId, Collectors.counting()));

            // 找到最大计数用于归一化
            long maxCount = venueCounts.values().stream().max(Long::compare).orElse(1L);

            Map<Integer, Double> venueScores = new HashMap<>();
            for (Map.Entry<Integer, Long> venueEntry : venueCounts.entrySet()) {
                // 归一化到0-1之间
                double score = (double) venueEntry.getValue() / maxCount;
                venueScores.put(venueEntry.getKey(), score);
            }

            userVenueScores.put(userId, venueScores);
        }
    }

    // 计算场地之间的余弦相似度
    private void calculateVenueSimilarities() {
        // 获取所有场地ID
        Set<Integer> allVenues = allReservations.stream()
                .map(ReservationExcel::getVenueId)
                .collect(Collectors.toSet());

        // 为每个场地构建特征向量（基于用户评分）
        Map<Integer, Map<Integer, Double>> venueUserVectors = new HashMap<>();

        // 初始化
        for (int venueId : allVenues) {
            venueUserVectors.put(venueId, new HashMap<>());
        }

        // 填充向量
        for (Map.Entry<Integer, Map<Integer, Double>> userEntry : userVenueScores.entrySet()) {
            int userId = userEntry.getKey();
            Map<Integer, Double> venueScores = userEntry.getValue();

            for (Map.Entry<Integer, Double> venueEntry : venueScores.entrySet()) {
                int venueId = venueEntry.getKey();
                double score = venueEntry.getValue();
                venueUserVectors.get(venueId).put(userId, score);
            }
        }

        // 计算场地之间的相似度
        for (int venue1 : allVenues) {
            Map<Integer, Double> vector1 = venueUserVectors.get(venue1);
            Map<Integer, Double> similarities = new HashMap<>();

            for (int venue2 : allVenues) {
                if (venue1 == venue2) {
                    similarities.put(venue2, 1.0); // 自相似度为1
                    continue;
                }

                Map<Integer, Double> vector2 = venueUserVectors.get(venue2);

                // 计算余弦相似度
                double dotProduct = 0.0;
                double norm1 = 0.0;
                double norm2 = 0.0;

                // 收集所有共同的用户
                Set<Integer> commonUsers = new HashSet<>(vector1.keySet());
                commonUsers.retainAll(vector2.keySet());

                // 如果没有共同用户，相似度为0
                if (commonUsers.isEmpty()) {
                    similarities.put(venue2, 0.0);
                    continue;
                }

                for (int userId : commonUsers) {
                    double score1 = vector1.get(userId);
                    double score2 = vector2.get(userId);
                    dotProduct += score1 * score2;
                    norm1 += score1 * score1;
                    norm2 += score2 * score2;
                }

                norm1 = Math.sqrt(norm1);
                norm2 = Math.sqrt(norm2);

                double similarity = (norm1 * norm2 == 0) ? 0 : dotProduct / (norm1 * norm2);
                similarities.put(venue2, similarity);
            }

            venueSimilarities.put(venue1, similarities);
        }
    }

    // 为目标用户生成推荐
    public ReservationExcel generateRecommendation() {
        // 获取目标用户的预约记录
        List<ReservationExcel> userReservations = allReservations.stream()
                .filter(r -> r.getUserId() == targetUserId)
                .collect(Collectors.toList());

        // 如果用户预约记录少于50条，使用热门推荐
        if (userReservations.size() < 50) {
            return recommendPopular();
        }

        // 否则使用基于物品的协同过滤
        buildUserVenueMatrix();
        calculateVenueSimilarities();

        // 获取用户预约过的场地
        Set<Integer> userVenues = userReservations.stream()
                .map(ReservationExcel::getVenueId)
                .collect(Collectors.toSet());

        // 计算用户未预约过的场地的得分
        Map<Integer, Double> venueScores = new HashMap<>();

        for (int venueId : venueSimilarities.keySet()) {
            if (userVenues.contains(venueId)) {
                continue; // 跳过用户已经预约过的场地
            }

            double score = 0.0;
            for (int userVenue : userVenues) {
                double similarity = venueSimilarities.get(userVenue).get(venueId);
                double userPreference = userVenueScores.getOrDefault(targetUserId, new HashMap<>())
                        .getOrDefault(userVenue, 0.0);
                score += similarity * userPreference;
            }

            venueScores.put(venueId, score);
        }

        // 如果没有合适的推荐，回退到热门推荐
        if (venueScores.isEmpty()) {
            return recommendPopular();
        }

        // 找到得分最高的场地
        int recommendedVenueId = Collections.max(venueScores.entrySet(), Map.Entry.comparingByValue()).getKey();

        // 为该场地推荐一个合适的时间段
        return recommendTimeSlot(recommendedVenueId, userReservations);
    }

    // 热门推荐（基于所有用户的预约记录）
    private ReservationExcel recommendPopular() {
        // 找出最热门的场地
        Map<Integer, Long> venuePopularity = allReservations.stream()
                .collect(Collectors.groupingBy(ReservationExcel::getVenueId, Collectors.counting()));

        int popularVenueId = Collections.max(venuePopularity.entrySet(), Map.Entry.comparingByValue()).getKey();

        // 找出该场地最热门的时间段
        Map<String, Long> timeSlotPopularity = allReservations.stream()
                .filter(r -> r.getVenueId() == popularVenueId)
                .collect(Collectors.groupingBy(ReservationExcel::getTimeSlot, Collectors.counting()));

        String popularTimeSlot = Collections.max(timeSlotPopularity.entrySet(), Map.Entry.comparingByValue()).getKey();

        // 找出该场地最热门的星期几
        Map<String, Long> dayPopularity = allReservations.stream()
                .filter(r -> r.getVenueId() == popularVenueId)
                .collect(Collectors.groupingBy(ReservationExcel::getDayOfWeek, Collectors.counting()));

        String popularDay = Collections.max(dayPopularity.entrySet(), Map.Entry.comparingByValue()).getKey();

        // 创建推荐记录（使用下周的同一天）
        LocalDate nextWeek = LocalDate.now().plusWeeks(1);
        LocalDate recommendedDate = nextWeek.with(java.time.DayOfWeek.valueOf(popularDay.toUpperCase()));

        // 设置时间段
        LocalTime startTime, endTime;
        if (popularTimeSlot.equals("morning")) {
            startTime = LocalTime.of(9, 0);
            endTime = LocalTime.of(11, 0);
        } else if (popularTimeSlot.equals("afternoon")) {
            startTime = LocalTime.of(14, 0);
            endTime = LocalTime.of(16, 0);
        } else {
            startTime = LocalTime.of(19, 0);
            endTime = LocalTime.of(21, 0);
        }

        return new ReservationExcel(0, targetUserId, popularVenueId, startTime, endTime, recommendedDate);
    }

    // 为指定场地推荐时间段（基于用户历史偏好）
    private ReservationExcel recommendTimeSlot(int venueId, List<ReservationExcel> userReservations) {
        // 分析用户偏好的时间段
        Map<String, Long> userTimeSlotPref = userReservations.stream()
                .collect(Collectors.groupingBy(ReservationExcel::getTimeSlot, Collectors.counting()));

        String preferredTimeSlot = Collections.max(userTimeSlotPref.entrySet(), Map.Entry.comparingByValue()).getKey();

        // 分析用户偏好的星期几
        Map<String, Long> userDayPref = userReservations.stream()
                .collect(Collectors.groupingBy(ReservationExcel::getDayOfWeek, Collectors.counting()));

        String preferredDay = Collections.max(userDayPref.entrySet(), Map.Entry.comparingByValue()).getKey();

        // 创建推荐记录（使用下周的同一天）
        LocalDate nextWeek = LocalDate.now().plusWeeks(1);
        LocalDate recommendedDate = nextWeek.with(java.time.DayOfWeek.valueOf(preferredDay.toUpperCase()));

        // 设置时间段
        LocalTime startTime, endTime;
        if (preferredTimeSlot.equals("morning")) {
            startTime = LocalTime.of(9, 0);
            endTime = LocalTime.of(11, 0);
        } else if (preferredTimeSlot.equals("afternoon")) {
            startTime = LocalTime.of(14, 0);
            endTime = LocalTime.of(16, 0);
        } else {
            startTime = LocalTime.of(19, 0);
            endTime = LocalTime.of(21, 0);
        }

        return new ReservationExcel(0, targetUserId, venueId, startTime, endTime, recommendedDate);
    }


}